﻿using FlexField.Metadata;
using FlexField.Metadata.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// Contains extension methods to <see cref="MetadataBuilder"/> for adding entity framework stores.
    /// </summary>
    public static class MetadataEntityFrameworkBuilderExtensions
    {
        /// <summary>
        /// Adds an Entity Framework implementation of identity information stores.
        /// </summary>
        /// <typeparam name="TContext">The Entity Framework database context to use.</typeparam>
        /// <param name="builder">The <see cref="MetadataBuilder"/> instance this method extends.</param>
        /// <returns>The <see cref="MetadataBuilder"/> instance this method extends.</returns>
        public static MetadataBuilder AddEntityFrameworkStores<TContext>(this MetadataBuilder builder)
            where TContext : DbContext
        {
            var contextType = typeof(TContext);
            if (FindGenericBaseType(contextType, typeof(MetadataFieldContext<>)) != null)
            {
                builder.Services.TryAddScoped(typeof(IFieldStore<>).MakeGenericType(builder.KeyType),
                    typeof(DataFieldStore<,>).MakeGenericType(contextType, builder.KeyType));
            }

            if (FindGenericBaseType(contextType, typeof(MetadataDbContext<>)) != null)
            {
                builder.Services.TryAddScoped(typeof(IEntityStore<>).MakeGenericType(builder.KeyType),
                   typeof(DataEntityStore<,>).MakeGenericType(contextType, builder.KeyType));
            }

            return builder;
        }

        private static Type FindGenericBaseType(Type currentType, Type genericBaseType)
        {
            var type = currentType;
            while (type != null)
            {
                var genericType = type.IsGenericType ? type.GetGenericTypeDefinition() : null;
                if (genericType != null && genericType == genericBaseType)
                {
                    return type;
                }
                type = type.BaseType;
            }
            return null;
        }
    }
}
